結束上一篇30天Flutter手滑系列 - 導航與路由(Navigation & Routing),我們透過範例,示範了在路由跳轉時傳遞參數,但我們都使用的是StatelessWidget去達成目的。如果這時候有個輸入表單,那就需要用StatefulWidget才能做到。
在我們進入進階的狀態管理前,先來認識跟狀態有關的這兩個Widgets吧。
StatelessWidget在App初始化之後就不能改變,它是immutable。如果想要改變就得new一個新的StatelessWidget去做更換。
常見的StatelessWidget有:
延續之前的範例,我們來看一下其結構。
可以看到在整個Widget內都是使用靜態的Widget去做Navigation,過程中也不需要做任何狀態更新,應該是很好理解的。
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
        child: RaisedButton(
          child: Text('Register'),
          onPressed: () {
            Navigator.of(context).pushNamed('/register',
                arguments: {'name': 'Raymond'}).then((value) {
              // 新增第二個變數arguments
              showDialog(
                  // 新增一個對話框,用來顯示回傳的值
                  context: context,
                  child: AlertDialog(
                    content: Text(value),
                  ));
            });
          },
        ),
      ),
    );
  }
}
StatefulWidget可以在App內無限次的被重繪集及更新狀態,它是mutable,需要重繪時可以調用setState(),去標記自己為dirty狀態,為下次更新做準備。
常見的StatefulWidget有:
讓我們改寫範例,把傳遞參數部分透過使用者自行輸入名稱的方式去動態改變,因此我們需要改成Stateful Widget去操作這部分。
首先,定義一個StatefulWidget,然後把原本的MyHomePage變成一個私有的State,並繼承自MyHomePage這個StatefulWidget。
然後在_MyHomePageState中加入TextField,而要獲取TextField的值有兩種方法,透過TextEditingController,或是onChanged回傳,在這我用前者。
定義了controller後就可以獲取TextField中的值,因此在原本Navigation的參數部分,就可以直接引用controller的值了。
class MyHomePage extends StatefulWidget {   // 把MyHomePage設為一個StatefulWidget
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {      // 定義一個私有的Class,繼承自MyHomePage
  final TextEditingController _controller = TextEditingController();  // 定義一個controler
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Container(
            width: 200.0,
            margin: EdgeInsets.only(bottom: 10),
            child: TextField(
              controller: _controller,    // 利用controller去獲取TextField的值
            ),
          ),
          RaisedButton(
            child: Text('Register'),
            onPressed: () {
              Navigator.of(context).pushNamed('/register',
                  arguments: {'name': _controller.text}).then((value) {   // 參數接收來自TextField controller的值
                // 新增第二個變數arguments
                showDialog(
                    // 新增一個對話框,用來顯示回傳的值
                    context: context,
                    child: AlertDialog(
                      content: Text(value),
                    ));
              });
            },
          )
        ],
      )),
    );
  }
}
範例展示:
- 盡可能用Stateless。
- Stateful Wigets盡量在越低層級的節點使用,避免過多不必要的重build。
setState((){})會觸發State.build,如果你的觸發是在根節點,會造成所有Widgets被重build,會是個效能問題。- 需要重build的Widget,盡量節點數越少越好。
結束了10天基礎的UI Widgets介紹,明天會開始介紹較為進階的狀態管理(State Management)。
https://flutter.dev/docs/development/ui/interactive
https://lizhaoxuan.github.io/2019/01/02/Flutter-%E4%BD%A0%E8%BF%98%E5%9C%A8%E6%BB%A5%E7%94%A8StatefulWidget%E5%90%97/
https://medium.com/flutter-community/flutter-stateful-vs-stateless-db325309deae
盡量在子節點使用 Stateful。有點不懂 XD
這邊我稍微改一下語句,基本上應該是跟第三點有關XD
盡量減少setState重build的wigets數量是比較理想的,所以如果有狀態需要更新,盡量在越低層級的節點去做更新比較好。
如下圖,橘色框的重build的wigets會比紅色框內的少。